package dk.itu.KrakMap;

import java.util.*;

/**
 *
 * @author ITU-KF04-2011, Group 11
 */
public class RoutePlanRequest {

    public LinkedList<KrakNode> places = new LinkedList<KrakNode>();
    private HashSet<TravelType> travellerProfile = new HashSet<TravelType>();
    private boolean ordered = true;
    private boolean startLocked = true;
    private boolean endLocked = true;

    /**
     * get places marked by the user.
     * @return a LinkedList containing the users marked places as KrakNodes
     */
    public LinkedList<KrakNode> getPlaces() {
        return places;
    }

    /**
     * add a place.
     * @param places the place to set
     */
    public void addPlace(KrakNode place) {
        this.places.add(place);
    }

    /**
     * remove a place.
     * @param removes the place from places
     */
    public void removePlace(KrakNode place) {
        this.places.remove(place);
    }

    /**
     * check if a node already exists in places.
     * @param kn KrakNode to check for
     * @return true if it already exists in places.
     */
    public boolean containsNode(KrakNode kn) {
        return this.places.contains(kn);
    }

    /**
     * get all traveltypes from this request.
     * @return the travellerProfile
     */
    public HashSet<TravelType> getTravellerProfile() {
        return new HashSet<TravelType>(travellerProfile);
    }

    /**
     * check whether two travellerProfiles
     * @param profile HashSet<TravelType> to check with this.
     * @return true if there's a match.
     */
    public boolean hasTravellerProfile(HashSet<TravelType> profile) {
        return travellerProfile.equals(profile);
    }

    /**
     * add a new travellerprofile.
     * @param t the travellerProfile to add.
     */
    public void addTravelType(TravelType t) {
        travellerProfile.add(t);
    }

    /**
     * remove a TravelType.
     * @param t TravelType to be removed.
     */
    public void removeTravelType(TravelType t) {
        travellerProfile.remove(t);
    }

    /**
     * empty list of travellerprofiles.
     */
    public void clearTravellerProfile() {
        travellerProfile.clear();
    }

    /**
     * @return the ordered
     */
    public boolean isOrdered() {
        return ordered;
    }

    /**
     * set status of ordered.
     * @param ordered the ordered to set
     */
    public void setOrdered(boolean ordered) {
        this.ordered = ordered;
    }

    /**
     * get the status of startLocked. Does the route have a specific startPoint.
     * @return the startLocked
     */
    public boolean isStartLocked() {
        return startLocked;
    }

    /**
     * set status of startLocked. should there be a specific startpoint.
     * @param startLocked the startLocked to set
     */
    public void setStartLocked(boolean startLocked) {
        this.startLocked = startLocked;
    }

    /**
     * get the status of endLocked. Does the route have a specific endPoint.
     * @return the endLocked
     */
    public boolean isEndLocked() {
        return endLocked;
    }

    /**
     * set status of endLocked. should there be a specific endpoint.
     * @param endLocked the endLocked to set
     */
    public void setEndLocked(boolean endLocked) {
        this.endLocked = endLocked;
    }

    /**
     * Get the number of points in the request
     * @return The number of points in the request
     */
    public int size() {
        return this.places.size();
    }

    /**
     * check if it is possible to start finding quickest route.
     * @return true if a travellerprofile and a minimum of 2 points is included in the request.
     */
    boolean isReady() {
        return travellerProfile.size() > 0 && places.size() > 1;
    }

    /**
     * get the highest maxSpeed of all the travellerprofiles included in the request.
     * @return highest maxSpeed.
     */
    public int getMaxSpeed() {
        int maxSpeed = 0;
        for (TravelType t : travellerProfile) {
            if (t.getMaxSpeed() > maxSpeed) {
                maxSpeed = t.getMaxSpeed();
            }
        }
        return maxSpeed;
    }

    /**
     * Return the nearest node in the RoutePlanRequest
     * @param point The point to compare with
     * @return The nearest KrakNode
     */
    KrakNode getNearestNode(MapPoint point) {
        KrakNode res = null;
        double dist = -1;
        for (KrakNode n : places) {
            double tempDist = point.dist(n.getMapPoint());
            if (dist == -1 || tempDist < dist) {
                dist = tempDist;
                res = n;
            }
        }
        return res;
    }

    /**
     * replace a point in the request with a new one.
     * @param lastNode KrakNode to be replaced.
     * @param newNode KrakNode to be inserted instead of the old one.
     */
    public void replace(KrakNode lastNode, KrakNode newNode) {
        int index = places.indexOf(lastNode);
        places.set(index, newNode);
    }

    /**
     * insert a point a specific place in the request before another point.
     * @param before KrakNode to be moved one place back.
     * @param insert KrakNode to be inserted at the previous node's place.
     */
    void insertBefore(KrakNode before, KrakNode insert) {
        places.add(places.indexOf(before), insert);
    }

    /**
     * check for each of the traveltypes in the request if they exclude a specific RoadType.
     * @param rt RoadType to be searched for
     * @return true if one of the TravelTypes doesn't contain the RoadType
     */
    boolean canTravel(RoadType rt) {
        for (TravelType t : travellerProfile) {
            if (!t.getExcludedRoadTypes().contains(rt)) {
                return true;
            }
        }
        return false;
    }

    /**
     * swap two points in the request.
     * @param index1 int index of the first place to swap
     * @param index2 int index of the second place to swap
     * @return true if the places has succesfully been swapped.
     * return false if index is too high/low of element == null.
     */
    boolean swapPoints(int index1, int index2) {
        if (index1 < 0 || index1 >= places.size() || index2 < 0 || index2 >= places.size()) {
            return false;
        }
        KrakNode element1 = places.get(index1);
        KrakNode element2 = places.get(index2);
        if (element1 == null || element2 == null) {
            return false;
        }
        places.set(index1, element2);
        places.set(index2, element1);
        return true;
    }
}
